#    This file is part of SRIS.
#
#    SRIS is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    SRIS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with SRIS.  If not, see <http://www.gnu.org/licenses/>.
#
#    Copyright 2011 David Irvine
#

import uuid
import os
import json
import logging

from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django import forms

from sris.models import *

## Returns a list of file systems to scan to the client.  The scanner client loads this data at startup,
# and iterates through it scanning each file system, and uploading the results back to the server. 
#
#\param scanner The name of the scanner as passed by the client.
# Each scanner has its own unique id, this is a string value, and forms part of the URL, this is used
# to filter the filesystems returned to include only ones to be scanned by that specific instance. 
def listFileSystems(request,scanner=None):
	s=get_object_or_404(Scanner, name=scanner)
	# array of dictionaries, each dict containing the URL and ID of the filesystem.
	obs=[]
	# For each file system to be scanned, add a dictionary to the array containing the URL and ID.
	for fs in s.filesystems.all():
		obs.append({'URL':fs.URL, 'id':fs.id})
	##\returns An array of json objects, each object contains two keys, 
	# URL: A string containing the URL of the filesystem
	# and id, an integer representing the numerical ID of the filesystem inside the scanning system.
	# This is rendered to the client using the python json class, with pretty printing enabled.  The 
	# mime type used is text/plain.
	#
	return HttpResponse(json.dumps(obs,sort_keys=True, indent=4), mimetype="text/plain")

## Creates a new Scan and returns the ID to the caller.  Each time a scanner starts a run, it must
#  upload the data to the server.  As the data file may be many gigabytes in size, the files are
#  chunked into more manageable amounts.  As a result this requires multiple operations, and
#  the client must have a way of determining which scan is being uploaded.  Thus at the start of
#  each scan, the scanning client requests a new scan ID.  The createScan method then creates
#  a new scan in the database, and returns the ID as json data.
#
#  \param fs_id, the ID of the filesystem being scanned
def createScan(request,fs_id):
	fs=get_object_or_404(FileSystem, pk=fs_id)
	scan=fs.scans.create()
	scan.save()
	## \returns scan_id, the id to use when uploading data.
	data={"scan_id":scan.id}
	return HttpResponse(json.dumps(data,sort_keys=True, indent=4), mimetype="text/plain")


## uploadChunk uploads a chunked data file contained in the post data form the client and saves
#  it on the server.  The post data must contain a gripped file, the scan_id must match a
#  previously created scan.  
#
#  The uploaded data file is saved onto the server and a new DataFile created for the scan.
#  \param scan_id The ID of a previously created scan.
#  \param POST an array of json objects gzipped.
@csrf_exempt
def uploadChunk(request, scan_id):
	scan=get_object_or_404(Scan, pk=scan_id)
	# Check the scan is actually being scanned and not already finalized.
	if scan.state!="SCANNING":
		return HttpResponseBadRequest("Scan ID invalid")

	# Check there is post data.
	if request.method!='POST':
		return HttpResponseBadRequest("POST Data expected")

	# Create a new data file for the scan.
	df=scan.dataFiles.create()
	fname="scan-%s-%s.json.gz"% (scan.id, str(uuid.uuid4()))
	df.fileName=fname
	df.save()

	# Open the file, and write the POST data to it.
	f=open(os.path.join(settings.SCANPATH,fname),'wb')
	f.write(request.raw_post_data)
	f.close()

	return HttpResponse("OK",mimetype="text/plain")



## finalizeScan adds the required metadata to the scan object and marks the scan as complete.  This
#  is done after a new scan has been scanned and all data files are uploaded.  It marks the scan
#  as SCANNED, after which point processing can then begin.
#
#\param filesScanned - The total number of files scanned 
#\param filesUnread - The number of files that could not be read (i.e. due to permissions problems)
#\param vfsData.size - The size of the file system in bytes
#\param vfsData.free - The number of free bytes on the file system
#\param vfsData.filesListed - The number of files listed as stored on the file system.
#\param vfsData.optional - object of optional data.

@csrf_exempt
def finalizeScan(request, scan_id):
	scan=get_object_or_404(Scan, pk=scan_id)
	if request.method!='POST':
		return HttpResponseBadRequest("POST Data expected")
	data=json.loads(request.raw_post_data)
	scan.filesScanned=data['filesScanned']
	scan.filesUnread=data['filesUnread']
	vfsdata=data['vfsData']
	scan.vfsSize=vfsdata['size']
	scan.vfsFree=vfsdata['free']
	scan.vfsFilesListed=vfsdata['filesListed']
	for key,value in vfsdata['optional'].iteritems():
		o=scan.VFSExtendedInfo.create()
		o.name=key
		o.value=value
		o.save()
	scan.state="SCANNED"
	scan.save()
	return HttpResponse("OK")
